/**
 * Copyright (c) suzuki ryoutoku
 * See this code regarding the Apache License Version 2.0
 * @file Reader.cpp
 */
//=============================================================================
//	pre processor
//=============================================================================

#include "Reader.h"
#include "Graphics.h"
#include <sstream>
#include <iomanip>
using namespace library::graphics;
using namespace library::log;
using namespace std;
using namespace library::max;

//=============================================================================
//	define's local class
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @date 2007-12-16
 * @version 1.0
 */
struct readTriangle
{
  int index[3];
  int m_group_index;
};

/**
 * @author suzuki ryoutoku
 * @date 2007-12-16
 * @version 1.0
 */
struct readSquare
{
  int index[4];
  int m_group_index;
};

/**
 * @author suzuki ryoutoku
 * @date 2007-12-16
 * @version 1.0
 */
struct ReadGroup
{
  int m_index;
  list<int> m_triangles;
  list<int> m_squares;
  ReadGroup(){
	m_index = 0;
  }
  ~ReadGroup(){
	m_triangles.clear();
	m_squares.clear();
  }
};


//=============================================================================
//	Reader's class member
//=============================================================================
/**
 * sum of character per one line
 * @author suzuki ryoutoku 
 */
const int Reader::NumberOfCharactersLine = 1024;

/**
 * It is comment symbol.
 * @author suzuki ryoutoku
 */
const char* Reader::Comment = "#";

//=============================================================================
//	Reader's method
//=============================================================================
/**
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
Reader::Reader(library::log::LogManager* arg)
{
  this->m_sum_vertex = 0;
  this->m_sum_volume = 0;
  this->m_sum_innernode = 0;
  this->m_sum_outerpatch = 0;
  this->m_sum_innerpatch = 0;
  this->m_sum_inneredge = 0;
  this->m_log_manager = arg;
}

/**
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
Reader::~Reader()
{
  if( this->m_fin.is_open() != 0 ){
	this->m_fin.close();
  }
}


/**
 * To open file
 * @param filename file name
 * @return if it'sfalse, failure occurs
 * @version 1.0
 * @author suzuki ryoutoku
 */
bool Reader::openFile(const string& filename)
{
  this->m_fin.open(filename.c_str());
  if( !this->m_fin ) return false;
  return true;
}

/**
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
bool Reader::readVertexInfomation(void)
{
  char buffer[NumberOfCharactersLine];
  
  while( this->m_fin.getline( buffer, NumberOfCharactersLine ) ){
	// convert string parameter
	string line(buffer);

	// cutting character end of line.
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}

	// skip blank line
	if( !Reader::CheckLine(&line) ) continue;
	
	// count data sum
	stringstream stout(line);
	switch( Reader::CountData(line," ") ){
	case 1: // when vertex sum
	  if( stout >> this->m_sum_vertex ){
		return true;
	  }
	  break;
	case 3: // when vertex sum, inner node sum, volume sum 
	  stout >> this->m_sum_vertex 
			>> this->m_sum_innernode 
			>> this->m_sum_volume;
	  return true;
	default:
	  return false;
	}
	
	break;
  }
  return false;
}

/**
 * To read vertex coordinate in file
 * @param arg It is stored vertex array
 *
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
bool Reader::readVertex(Vector<double>* arg)
{
  char buffer[NumberOfCharactersLine];
  int count = 0;
  
  while( this->m_fin.getline( buffer, NumberOfCharactersLine ) ){
	string line(buffer);
	// cutting character end of line
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	// cutting comment
	if( !Reader::CheckLine(&line) ) continue;

	// read vertex array
	stringstream stout(line);
	float x,y,z;
	if( !( stout >> x >> y >> z ) ){
	  return false;
	}else{
	  arg[count] = Vector<double>( x, y, z , 1.0);
	  count ++;
	}
	
	// when count equal vertex sum
	if( count >= this->m_sum_vertex ){
	  break;
	}
  }
  
  // inner node times empty reading
  for( unsigned int i = 0; i < this->m_sum_innernode; i++){
	this->m_fin.getline( buffer, NumberOfCharactersLine );
	string line(buffer);
	// cutting character end of line.
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	// cutting comment
	if( !Reader::CheckLine(&line) ) continue;
  }
  return true;
}

/**
 * To skip vertex coordinate part in file
 * @author suzuki ryoutoku 
 * @date 2007-10-27
 * @version 1.0
 */
bool Reader::jumpVertexInPCMFile(void)
{
  char buffer[NumberOfCharactersLine];
  int count = 0;
  // empty reading
  for( unsigned int i = 0; i < this->m_sum_vertex; i++){
	this->m_fin.getline( buffer, NumberOfCharactersLine );
	string line(buffer);
	// cutting character end of line
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	// cutting comment
	if( !Reader::CheckLine(&line) ) continue;
  }
  
  // inner node
  for( unsigned int i = 0; i < this->m_sum_innernode; i++){
	this->m_fin.getline( buffer, NumberOfCharactersLine );
	string line(buffer);
	// cutting character end of line
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	// cutting comment
	if( !Reader::CheckLine(&line) ) continue;
  }
  return false;
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-22
 */
bool Reader::allocateVolume(Model* arg)
{
  if( this->Reader::m_sum_volume == 0 ){
	return false;
  }
  arg->m_volume_sum = this->m_sum_volume;
  arg->m_volume = new Volume[arg->m_volume_sum];
  return true;
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-22
 */
bool Reader::allocateVertex(Model* arg)
{
  if( this->Reader::m_sum_vertex == 0 ){
	return false;
  }
  arg->m_vertex_sum = this->m_sum_vertex;
  arg->m_vertex = new Vector<double>[arg->m_vertex_sum];
  return true;
}

/**
 * Then there are texture infomations in 3d model data, 
 * please you override this method.
 * \note
 * This method always push false. If there are texture infomations,
 * sub class must override this method.
 *
 * @return true means to require creating texture, flase means no.
 * 
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-25
 */
bool Reader::isRequiredCreatingImageDatas(void)
{
  return false;
}

/**
 * This method is virtual method.
 * Then there are texture infomations in 3d model data, 
 * please you override this method.
 * \note
 * If you override this method,
 * your code is corresponding image datas to require for model data with 
 * contents of ImageDataManager.
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-25
 */
void Reader::createImageDatas(Model* arg, ImageDataManager* imd)
{
  return;
}

//=============================================================================
//	Reader's class method
//=============================================================================
/**
 * To check line, if necessary remove comment
 *
 * @return false invalid line
 * 
 * @param arg eval string
 *
 * @author suzuki ryoutoku
 * @version 1.0
 */
bool Reader::CheckLine(string* arg)
{
  // cutting comment
  if( string::npos != arg->find(string(Comment)) ){
	arg->erase( arg->find(string(Comment)) );
  }
  
  if( !arg->size() ) return false;
  
  return true;
}

/**
 * To examin how many data partitioned by the delimiter there is.
 * @param arg 
 * @param specify " " in defalut
 * @return number of data + 1
 * @version 1.0
 * @date 2006-04-26
 */
int Reader::CountData(const string& arg, const string specify)
{
  size_t start_point = 0;
  int count = 0;
  size_t pre_point = -1;// When One digit, process can go well.
  while( string::npos != ( start_point = arg.find(specify, start_point) ) ){
	if( arg.find(specify, start_point) == (arg.size()-1) ){
	  break;
	}

	// If specified character is continuousness, no count.
	if( pre_point != start_point-1 ){
	  count++;
	}
	pre_point = start_point;
	start_point += 1;

  }
  return (count + 1);
}


/**
 * This method extract model name from file path involved directory.
 * @version 1.0
 * @date 2006-08-22
 */
void Reader::ExtractModelName(const string& arg, char* model)
{
  size_t end_pos = arg.rfind(string("."));
  size_t start_pos = 0;
  
  char* tmp = (char*)arg.c_str();
  
  
  while( NULL != (tmp = strstr(tmp, "\\") ) ){
	tmp += 1;
	start_pos = tmp - arg.c_str();
  }
  
  for( size_t i = start_pos; i < end_pos; i ++){
	model[i - start_pos] = arg.c_str()[i];
  }
  model[end_pos-start_pos] = '\0';
}

//=============================================================================
//	ReadPcm's method
//=============================================================================
/**
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
ReadPcm::ReadPcm(library::log::LogManager* arg)
  : Reader(arg)
{
  m_face_group_index = NULL;
  m_face_sum = 0;
}


/**
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
ReadPcm::~ReadPcm()
{
  if( m_face_group_index != NULL )delete [] m_face_group_index;

}

/**
 * This method is called outside.
 * 
 * @param filename is name of pcm file 
 *
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
Model* ReadPcm::readPCMFile(const string& filename)
{
  char buffer[NumberOfCharactersLine];
  unsigned int i,j,k, l;
  char tmp[256];

  // check file name
  if( !filename.rfind(string(".PCM")) || !filename.rfind(string(".pcm")) ){
	return NULL;
  }
  strcpy( this->Reader::m_filename, filename.c_str());
  this->m_log_manager->print("Read file name = %s\nstart time =%s\n",
							 this->Reader::m_filename,
							 this->m_log_manager->storeTime(tmp));

  // first reading patch sum
  this->m_face_sum = this->readFaceSumInPCMFile(filename);
  if( this->m_face_sum == -1 ) return NULL;

  // To acquire memory a few minutes of the patch sum
  this->m_face_group_index = new int[ this->m_face_sum];
  memset(this->m_face_group_index,0,sizeof(int)*this->m_face_sum);

  // second reading pcg file, (patch group)
  this->readPCGFile(filename);

  if( !this->Reader::openFile(filename) ){
	return NULL;
  }

  if( !this->Reader::readVertexInfomation() ){
	return NULL;
  }

  // create model
  Model* model = new Model(false);
  this->Reader::allocateVolume(model);
  this->Reader::allocateVertex(model);
  if( !this->Reader::readVertex( model->m_vertex ) ){
	return NULL;
  }

  vector<readTriangle> tmp_triangle;
  vector<readSquare> tmp_quadrangle;
  vector<ReadGroup> tmp_group;
  int face_index = 0;

  for( i = 0; i < this->Reader::m_sum_volume; i++ ){
	Volume* volume = &(model->m_volume[i]);
	sprintf(volume->m_name, "volume%d", i);


	tmp_triangle.clear();
	tmp_quadrangle.clear();
	tmp_group.clear();
	
	// reading patch sum
	while( this->m_fin.getline( buffer, NumberOfCharactersLine ) ){
	  string line(buffer);
	  // cutting character end of line
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  // skip empty line
	  if( !Reader::CheckLine(&line) ) continue;
	  stringstream stout(line);
	  
	  stout >> this->Reader::m_sum_outerpatch 
			>> this->Reader::m_sum_innerpatch 
			>> this->Reader::m_sum_inneredge;
	  break;
	}

	// reading outer patch
	for( j = 0; j < this->Reader::m_sum_outerpatch ; j++ ){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	  switch( Reader::CountData(line, " ") ){
	  case 3:
		{
		  readTriangle arg;
		  stringstream stout(line);
		  // consider only colockwise!
		  stout >> arg.index[2] >> arg.index[1] >> arg.index[0];
		  arg.m_group_index = this->searchFaceGroupIndex(face_index);

		  // checking to belong to group
		  ReadGroup* group = NULL;
		  for( int k = 0; k < tmp_group.size(); k++){
			if( arg.m_group_index == tmp_group[k].m_index ){
			  group = &(tmp_group[k]);
			  break;
			}
		  }
		  if( group != NULL ){
			//already exsiting group
			group->m_triangles.push_back(tmp_triangle.size());
		  }else{
			//not yet existing group
			ReadGroup new_group;
			new_group.m_index = arg.m_group_index;
			new_group.m_triangles.push_back(tmp_triangle.size());

			tmp_group.push_back(new_group);
		  }
		  tmp_triangle.push_back(arg);
		  
		  face_index++;
		}
		break;
	  case 4:
		{
		  readSquare arg;
		  stringstream stout(line);
		  // consider only clockwise
		  stout >>arg.index[3] >> arg.index[2] >> arg.index[1] >> arg.index[0];
		  arg.m_group_index = this->searchFaceGroupIndex(face_index);

		  ReadGroup* group = NULL;
		  for( int k = 0; k < tmp_group.size(); k++){
			if( arg.m_group_index == tmp_group[k].m_index ){
			  group = &(tmp_group[k]);
			  break;
			}
		  }
		  if( group != NULL ){
			group->m_squares.push_back(tmp_quadrangle.size());
		  }else{
			ReadGroup new_group;
			new_group.m_index = arg.m_group_index;
			new_group.m_squares.push_back(tmp_quadrangle.size());
			tmp_group.push_back(new_group);
		  }
		  tmp_quadrangle.push_back(arg);
		  face_index++;
		}
		break;
	  }
	}
	cout << "reading outer patch end" << endl;

	// skip inner patch
	for( k = 0; k < this->Reader::m_sum_innerpatch; k ++){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	}
	
	// skip inner edge
	for( l = 0; l < this->Reader::m_sum_inneredge; l ++){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	}
	cout << "create group" << endl;

	// create group
	volume->m_group_sum = tmp_group.size();
	volume->m_group = new Group[volume->m_group_sum];
	for( int k = 0; k < volume->m_group_sum; k++ ){
	  volume->m_group[k].m_index = tmp_group[k].m_index;
	  volume->m_group[k].initialize(tmp_group[k].m_triangles.size(),
									tmp_group[k].m_squares.size());
	}

	cout << "triangle" << endl;
	//　Target is all triangle in this volume
	cout << tmp_triangle.size() << endl;
	for( int k = 0; k < volume->m_group_sum; k++ ){
	  int counter = 0;
	  for( int m = 0; m < tmp_triangle.size() ; m++ ){
		if( volume->m_group[k].m_index == tmp_triangle[m].m_group_index ){
		  volume->m_group[k].m_triangle[counter++] = tmp_triangle[m].index[0];
		  volume->m_group[k].m_triangle[counter++] = tmp_triangle[m].index[1];
		  volume->m_group[k].m_triangle[counter++] = tmp_triangle[m].index[2];
		}
		if( counter/3 == volume->m_group[k].m_triangle_sum ){
		  break;
		} 
	  }
	}
	cout << "quadrangle" << endl;
	// Target is all quadrangle in this volume
	for( int k = 0; k < volume->m_group_sum; k++ ){
	  int counter = 0;
	  for( int n = 0; n < tmp_quadrangle.size() ; n++ ){
		if( volume->m_group[k].m_index == tmp_quadrangle[n].m_group_index ){
		  volume->m_group[k].m_quadrangle[counter++]
			=tmp_quadrangle[n].index[0];
		  volume->m_group[k].m_quadrangle[counter++] 
			= tmp_quadrangle[n].index[1];
		  volume->m_group[k].m_quadrangle[counter++] 
			= tmp_quadrangle[n].index[2];
		  volume->m_group[k].m_quadrangle[counter++] 
			= tmp_quadrangle[n].index[3];
		}
		if( counter/4 == volume->m_group[k].m_quadrangle_sum ){
		  break;
		}
	  }
	}
	
  }

  // create normal vector
  model->createNormalVector();

  // create origin of local
  model->createCenter();

  this->Reader::m_log_manager->print("end time =%s\n",
									 this->m_log_manager->storeTime(tmp));
  return model;
}


/**
 * To read PCG file
 * \note
 * Pcg file is writed about face group.
 * When pcg file do not exist in same directory of pcm file,
 * return 0.
 * @retval 0 no PCG file.
 *
 * @author suzuki ryoutoku
 * @date 2007-10-24
 * @version 1.0
 */
int ReadPcm::readPCGFile(const string& filename){
  ifstream fin;
  int group_index = 0;
  string pcg_filename = filename;
  char buffer[Reader::NumberOfCharactersLine];

  if( string::npos != filename.rfind(string(".PCM")) ){
	pcg_filename.replace(filename.rfind(string(".PCM")), 4,
						 string(".pcg") );
  }else{
	pcg_filename.replace(filename.rfind(string(".pcm")), 4,
						 string(".pcg") );
  }
  cout << "pcg filename = " << pcg_filename << endl;

  fin.open(pcg_filename.c_str());

  if( !fin ) return 0;

  while( fin.getline( buffer, NumberOfCharactersLine ) ){
	string line(buffer);

	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	
	if( !Reader::CheckLine(&line) ) continue;
	
	// When discerning face group chunk.
	if( string::npos != line.find("faceGroupN") ){
	  stringstream stout(line);
	  char tmp[256];
	  int face_sum;
	  stout >> tmp >> face_sum;
	  cout << tmp << " " << face_sum << endl;
	  this->readFaceGroup(fin, group_index, face_sum);
	  group_index++;
	}
  }
  fin.close();
  cout << "Reading pcg file end" << endl;
  return 0;
}

/**
 * This method read face group infomation from pcg file.
 *
 * @param fin is file stream
 * @param group_index
 * @param face_sum is a number of face of pcm file
 *
 * @retval -1 can not acquire memory
 * @retval -2 file is failure
 *
 * @author suzuki ryoutoku
 * @date 2007-10-24
 * @version 1.0
 */
int ReadPcm::readFaceGroup(ifstream& fin, int group_index, int face_sum){
  int counter = 0;
  char buffer[Reader::NumberOfCharactersLine];

  while( fin.getline( buffer, NumberOfCharactersLine ) ){
	string line(buffer);
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	if( !Reader::CheckLine(&line) ) continue;
	stringstream stout(line);
	int tmp;
	stout >> tmp;

	// store group index in working buffer for face index
	this->m_face_group_index[tmp] = group_index;

	counter++;
	if( counter == face_sum ){
	  return 0;
	}
  }
  return -2;
}

/**
 *
 * @return group index
 *
 * @author suzuki ryoutoku
 * @date 2007-10-24
 * @version 1.0
 */
int ReadPcm::searchFaceGroupIndex(int face_index){
  return this->m_face_group_index[face_index];
}

/**
 * Count outer patch sum in pcm file
 *
 * @param filename PCM file name
 *
 * @return outer patch sum( when -1, failure occurs)
 *
 * @author suzuki ryoutoku 
 * @date 2007-10-27
 * @version 1.0
 */
int ReadPcm::readFaceSumInPCMFile(const string& filename)
{
  if( !this->Reader::openFile(filename) ){
	return -1;
  }

  this->Reader::readVertexInfomation();
  this->Reader::jumpVertexInPCMFile();


  char buffer[Reader::NumberOfCharactersLine];

  int face_sum = 0;
  for(int i = 0; i < this->Reader::m_sum_volume; i++ ){
	int sum_outerpatch;
	int sum_innerpatch; 
	int sum_inneredge;
	while( this->m_fin.getline( buffer, NumberOfCharactersLine ) ){

	  string line(buffer);

	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }

	  if( !Reader::CheckLine(&line) ) continue;
	  stringstream stout(line);
	  
	  stout >> sum_outerpatch 
			>> sum_innerpatch 
			>> sum_inneredge;
	  face_sum += sum_outerpatch;
	  break;
	}

	for( int j = 0; j < sum_outerpatch ; j++ ){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	}

	for(int k = 0; k < sum_innerpatch; k ++){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	}
	
	for(int l = 0; l < this->Reader::m_sum_inneredge; l ++){
	  this->m_fin.getline( buffer, NumberOfCharactersLine );
	  string line(buffer);
	  if( string::npos != line.rfind(string("\r")) ){
		line.erase( line.rfind(string("\r")) );
	  }
	  if( !Reader::CheckLine(&line) ) continue;
	}
  }

  this->m_fin.close();
  return face_sum;
}

//=============================================================================
//	Read3ds's method
//=============================================================================
/**
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
Read3ds::Read3ds(library::log::LogManager* arg)
  : Reader(arg)
{
}

/**
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
Read3ds::~Read3ds()
{
  vector<library::max::Object*>::iterator ite = m_objects.begin();
  while( ite != m_objects.end() ){
	delete (*ite);
	ite++;
  }
  m_objects.clear();
  vector<library::max::Material*>::iterator mite = m_material.begin();
  while( mite != m_material.end() ){
	delete (*mite);
	mite++;
  }
  m_material.clear();
}

/**
 * This method is called outside.
 * \note
 * @param filename is 3ds file involved absolute path.
 * @return If value was NULL, failure occurs.
 *
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
Model* Read3ds::read3DS(const string& filename)
{
  FILE* fp = fopen(filename.c_str(),"rb");
  char tmp[256];
  this->m_log_manager->print("Read file name = %s\nstart time =%s\n",
							 filename.c_str(),
							 this->m_log_manager->storeTime(tmp));

  Chunk fileChunk;
  unsigned int current_reading_size = 0;

  // First, This is first chunk read,it's stored id 0x4d4d and total file size.
  current_reading_size += fileChunk.readFile(fp);
  fileChunk.display();

  while( current_reading_size < fileChunk.m_length ){
	Chunk chunk;
	current_reading_size += chunk.readFile(fp);
	switch( chunk.m_ID ){
	case EDIT3DS:
	  this->readEdit3ds(fp, &chunk);
	  current_reading_size += (chunk.m_length-Chunk::DATASIZE);
	  break;
	default:
	  current_reading_size += chunk.skipFile(fp);
	}
  }
  Model* model =  createModelData();
  
  
  fclose(fp);

  this->Reader::m_log_manager->print("end time =%s\n",
									 this->m_log_manager->storeTime(tmp));
  return model;
}

/**
 * This method read a EDIT3DS chunk from 3ds file.
 *
 * @param fp must be 3ds file
 * @param head must be EDIT3DS's chunk header
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-13
 */
void Read3ds::readEdit3ds(FILE* fp, library::max::Chunk* head)
{
  unsigned int readsize = head->m_bytesread;
  while( readsize < head->m_length ){
	Chunk tmpChunk;
	readsize += tmpChunk.readFile(fp);
#if 0
	cout << "  ";
	tmpChunk.display();
#endif
	switch( tmpChunk.m_ID ){
	case OBJECT:
	  readObject(fp, &tmpChunk);
	  break;
	case MATERIAL:
	  readMaterial(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	readsize += tmpChunk.m_length;
  }
}

/**
 * This method reads object chunk in 3ds file.
 * @param fp must be 3ds file pointer
 * @param head must be object chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readObject(FILE* fp, library::max::Chunk* head)
{
  char buffer[256];
  max::Object* maxobj = new max::Object;  // create max::Object instance

  this->m_cobject.push(maxobj);
  // object must have name
  head->m_bytesread += getName(fp, buffer);
  cout << "   name=[" << buffer << "]" << endl;
  this->m_cobject.top()->setM_name(buffer);
  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
#if 0
	cout << "   ";
	tmpChunk.display();
#endif
	switch( tmpChunk.m_ID ){
	case OBJECT_MESH:
	  readMesh(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }
  this->m_cobject.pop();

  if( maxobj->isModelData() ){
	this->m_objects.push_back(maxobj);
  }else{
	delete maxobj;
  }

}

/**
 * This method reads mesh chunk in 3ds file.
 * @param fp 3ds file pointer
 * @param head mesh chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMesh(FILE* fp, library::max::Chunk* head)
{
  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
#if 0
	cout << "   ";
	tmpChunk.display();
#endif
	switch( tmpChunk.m_ID ){
	case MESH_VERTICES:
	  readMeshVertices(fp, &tmpChunk);
	  break;
	case MESH_FACES:
	  readMeshFaces(fp, &tmpChunk);
	  break;
	case MESH_MATERIAL:
	  readMeshMaterial(fp, &tmpChunk);
	  break;
	case MESH_TEXTURE_COORD:
	  readMeshTextureCoord(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }
}

/**
 * This method read MATERIAL chunk.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMaterial(FILE* fp, library::max::Chunk* head)
{
  char buffer[256];
  bool flag = false;
  if( this->m_cmaterial.empty() ){
	max::Material* mat = new max::Material;  // create max::Material instance
	this->m_material.push_back(mat);
	this->m_cmaterial.push(mat);
	flag = true;
  }


  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
#if 0
	cnout << "   ";
	tmpChunk.display();
#endif
	switch( tmpChunk.m_ID ){
	case MATERIAL_NAME:
	  if( flag == false ){
		max::Material* mat = new max::Material;// create max::Material instance
		this->m_material.push_back(mat);
		this->m_cmaterial.push(mat);
		flag = true;
	  }
	  tmpChunk.m_bytesread += getName(fp,buffer);
	  cout << "   name=[" << buffer << "]" << endl;
	  this->m_cmaterial.top()->setM_name(buffer);
	  break;
	case MATERIAL_DIFFUSE:
	  readDiffuse(fp, &tmpChunk);
	  break;
	case MATERIAL_TEXMAP:
	  // MATERIAL can calls itself recursivily.
	  readMaterial(fp, &tmpChunk);
	  break;
	case MATERIAL_TEXFILE:
	  tmpChunk.m_bytesread += getName(fp,buffer);
	  cout << "   texfile=[" << buffer << "]" << endl;
	  this->m_cmaterial.top()->setM_texture_filename(buffer);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }

  if( flag == false ){
	this->m_cmaterial.pop();
  }
}


/**
 * This method read MATERIAL_DIFFUSE chunk.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readDiffuse(FILE* fp, library::max::Chunk* head)
{
  Chunk tmpChunk;
  unsigned char r,g,b;
  tmpChunk.readFile(fp);
  tmpChunk.m_bytesread += fread(&r,1,sizeof(unsigned char),fp);
  tmpChunk.m_bytesread += fread(&g,1,sizeof(unsigned char),fp);
  tmpChunk.m_bytesread += fread(&b,1,sizeof(unsigned char),fp);
  cout << "   [rgb]";
  cout << setw(2) << setbase(16) 
	   << (unsigned int)r << ":" 
	   << setw(2) << setbase(16) 
	   << (unsigned int)g << ":" 
	   << setw(2) << setbase(16) 
	   << (unsigned int)b << endl;
  cout << setbase(10);
  this->m_cmaterial.top()->m_diffuse[0]=r;
  this->m_cmaterial.top()->m_diffuse[1]=g;
  this->m_cmaterial.top()->m_diffuse[2]=b;

  tmpChunk.skipFile(fp);
  
  head->m_bytesread += tmpChunk.m_length;
}

/**
 * This method reads vertex of mesh chunk in 3ds file.
 * @param fp 3ds file pointer
 * @param head vertex of mesh chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMeshVertices(FILE* fp, library::max::Chunk* head)
{
  unsigned short number_vertices = 0;
  head->m_bytesread += fread(&number_vertices,
							 1,
							 sizeof(unsigned short),
							 fp);

  cout << "   number of vertex = " << number_vertices << endl;

  if( 0 == number_vertices ) return;

  for(int i=0; i < number_vertices; i++){
	float coord[3];
	head->m_bytesread += fread(&(coord[0]), sizeof(float), 1, fp);
	head->m_bytesread += fread(&(coord[1]), sizeof(float), 1, fp);
	head->m_bytesread += fread(&(coord[2]), sizeof(float), 1, fp);
#if 0
	cout << "    "
		 << coord[0] << " "
		 << coord[1] << " "
		 << coord[2] << endl;
#endif
	this->m_cobject.top()->m_coord.push_back(coord[0]);
	this->m_cobject.top()->m_coord.push_back(coord[1]);
	this->m_cobject.top()->m_coord.push_back(coord[2]);
  }

  this->m_cobject.top()->m_vertexsum+=number_vertices;
}

/**
 * This method reads face of mesh chunk in 3ds file.
 * @param fp 3ds file pointer
 * @param head face of mesh chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMeshFaces(FILE* fp, library::max::Chunk* head)
{
  unsigned short number_faces = 0;
  head->m_bytesread += fread(&number_faces,
							 1,
							 sizeof(unsigned short),
							 fp);
  cout << "   number of faces = " << number_faces << endl;
  
  //  head->skipFile(fp);
  for(int i=0; i <  number_faces; i++){
	unsigned short index[3];
	unsigned short poly_flag;
	fread(&(index[0]), sizeof(unsigned short), 1, fp);
	fread(&(index[1]), sizeof(unsigned short), 1, fp);
	fread(&(index[2]), sizeof(unsigned short), 1, fp);
	fread(&poly_flag, sizeof (unsigned short), 1, fp);
	this->m_cobject.top()->m_index.push_back(index[0]);
	this->m_cobject.top()->m_index.push_back(index[1]);
	this->m_cobject.top()->m_index.push_back(index[2]);
  }
  this->m_cobject.top()->m_trianglesum=number_faces;
  // Face chunk have child chunk.
  // I recursively call readMesh();
  head->m_bytesread += number_faces*8;
  readMesh(fp, head);
}

/**
 * This method reads face of mesh chunk in 3ds file.
 * @param fp 3ds file pointer
 * @param head material of face chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMeshMaterial(FILE* fp, library::max::Chunk* head)
{
  char buffer[256];
  head->m_bytesread += getName(fp, buffer);
  unsigned short number_face;

  head->m_bytesread += fread(&number_face, 1, sizeof(unsigned short), fp);
  cout << "   number of faces in [" 
	   << buffer 
	   <<"]= " 
	   << number_face << endl;
  if( number_face == 0 ){
	head->skipFile(fp);
	return;
  }

  library::max::FaceGroup* fg = new FaceGroup();
  fg->setM_materialname(buffer);
  

  for( int i = 0; i < number_face; i++ ){
	unsigned short tmp;
	fread(&tmp, 1, sizeof(unsigned short), fp);
#if 0
	cout << "   f:" << tmp << endl;
#endif
	fg->m_faces.push_back(tmp);
  }
  if( 0 != fg->m_faces.size() ){
	this->m_cobject.top()->m_group.push_back(fg);
  }else{
	delete fg;
  }
}

/**
 * This method reads texture coords of mesh chunk in 3ds file.
 * @param fp 3ds file pointer
 * @param head  texture coords of mesh chunk's head infomation
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-14
 */
void Read3ds::readMeshTextureCoord(FILE* fp, library::max::Chunk* head)
{
  unsigned short number_texcoord = 0;
  head->m_bytesread += fread(&number_texcoord,
							 1,
							 sizeof(unsigned short),
							 fp);
  cout << "   number of texcoord = " << number_texcoord << endl;
  for(int i=0; i < number_texcoord; i++){
	float texcoord[2];
	head->m_bytesread += fread(texcoord, sizeof(float), 2, fp);
	this->m_cobject.top()->m_uv.push_back(texcoord[0]);
	this->m_cobject.top()->m_uv.push_back(texcoord[1]);
	//cout << "    " << texcoord[0] << "," << texcoord[1] << endl;
  }
  this->m_cobject.top()->m_uvsum = number_texcoord;
}

/**
 * This method build instance of class Model.
 * 
 * @return If value was NULL, failure occurs.
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-22
 */
Model* Read3ds::createModelData(void)
{
  this->Reader::m_sum_volume=this->m_objects.size();
  int number_of_uv=0;
  for( int i = 0; i < this->m_objects.size(); i++ ){
	this->Reader::m_sum_vertex+=this->m_objects[i]->m_vertexsum;
	number_of_uv += this->m_objects[i]->m_uvsum;
  }

  Model* model = new Model(false);
  this->Reader::allocateVolume(model);
  this->Reader::allocateVertex(model);
  if( number_of_uv != 0 ){
	if( number_of_uv != this->Reader::m_sum_vertex ){
	  number_of_uv = this->Reader::m_sum_vertex;
	}
	model->setM_uv_sum(number_of_uv);
	model->setM_uv(new Vector2D<float>[number_of_uv]);
  }
  

  int verglobid = 0;
  for( int i = 0; i < this->m_objects.size(); i++){
	library::max::Object* obj = this->m_objects[i];
	int versum = (int)(obj->m_vertexsum);
	obj->m_last_total_vertexsum = verglobid;
	for( int j = 0; j < versum; j++ ){
	  // Max's z coord is y coord in this.
	  model->m_vertex[verglobid].x = (double)(obj->m_coord[j*3+0]);
	  model->m_vertex[verglobid].y = (double)(obj->m_coord[j*3+2]);
	  model->m_vertex[verglobid].z = (double)(-1.0*obj->m_coord[j*3+1]);
	  model->m_vertex[verglobid].w = 1.0;

	  // If uv value is repeat
	  if( number_of_uv != 0 && obj->m_uvsum != 0 ){
		if( obj->m_uv[2*j+0] <= 1.0 ){
		  model->m_uv[verglobid].u = (obj->m_uv[2*j+0]);
		}else{
		  // it's repeat
		  model->m_uv[verglobid].u = (obj->m_uv[2*j+0] - 1.0f);
		}
		if( obj->m_uv[2*j+1] <= 1.0 ){
		  model->m_uv[verglobid].v = (1.0 - obj->m_uv[2*j+1]);
		}else{
		  model->m_uv[verglobid].v = (obj->m_uv[2*j+1] - 1.0f);
		}
	  }

	  verglobid++;
	}
  }

  // create volume
  for( int i = 0; i < this->m_objects.size(); i++ ){
	Volume* volume = &(model->m_volume[i]);
	library::max::Object* obj = this->m_objects[i];
	if( strlen(obj->m_name) != 0 ){
	  strcpy(volume->m_name, obj->m_name);
	}else{
	  char tmp[20];
	  sprintf(tmp,"v_%d", i);
	  strcpy(volume->m_name, tmp);
	}
	
	volume->setM_group_sum(obj->queryGroupSum());
	volume->m_group = new Group[volume->m_group_sum];

	// copy group
	vector<char> belong(obj->m_trianglesum);
	unsigned short count = 0;
	memset(&(belong[0]),0,obj->m_trianglesum);

	for( unsigned int j = 0; j < obj->m_group.size(); j++ ){
	  FaceGroup* fg = obj->m_group[j];
	  Group* group = &(volume->m_group[j]);

	  group->initialize(fg->m_faces.size(),
						0);
	  // set material index
	  try{
		unsigned int matindex = this->getMaterialIndex(fg->m_materialname);
		Color<float> color(this->m_material[matindex]->m_diffuse[0],
						   this->m_material[matindex]->m_diffuse[1],
						   this->m_material[matindex]->m_diffuse[2]);
		
		cout << color.r << " "
			 << color.g << " "
			 << color.b << " "
			 << color.a << endl;
		group->setM_color(color);
		// When face group uses texture file
		if( this->m_material[matindex]->m_texture_filename != '\0' ){
		  group->setM_texturefilename(this->m_material[matindex]->m_texture_filename);
		}
		group->setM_index(matindex);
	  }catch( char* e){
		cout << e;
		group->setM_index(-1);
	  }


	  for( unsigned int k = 0; k < fg->m_faces.size(); k ++ ){
		unsigned short index = fg->m_faces[k];
		belong[index]=1;
		count++;
		group->m_triangle[k*3+0]
		  =(int)(obj->m_index[index*3+0]+obj->m_last_total_vertexsum);
		group->m_triangle[k*3+1]
		  =(int)(obj->m_index[index*3+1]+obj->m_last_total_vertexsum);
		group->m_triangle[k*3+2]
		  =(int)(obj->m_index[index*3+2]+obj->m_last_total_vertexsum);
	  }
	}

	// There are faces to no belong group.
	if( count < obj->m_trianglesum ){
	  Group* group = &(volume->m_group[volume->m_group_sum-1]);
	  int tmp_face_sum=obj->m_trianglesum - count;

#ifdef DEBUG
	  cout << "["<<obj->m_name<<"]"<<endl;
	  cout << "There are faces to no belong group. A number of faces is "
		   << tmp_face_sum
		   <<endl;
#endif
	  
	  group->initialize(tmp_face_sum,
						  0);
	  group->setM_index(-1);
	  unsigned int counter=0;
	  for(unsigned int j = 0; j < belong.size(); j ++ ){
		if( belong[j] == 0 ){
		  group->m_triangle[counter*3+0]
			=(int)(obj->m_index[j*3+0]+obj->m_last_total_vertexsum);
		  group->m_triangle[counter*3+1]
			=(int)(obj->m_index[j*3+1]+obj->m_last_total_vertexsum);
		  group->m_triangle[counter*3+2]
			=(int)(obj->m_index[j*3+2]+obj->m_last_total_vertexsum);
#if 0
		  cout << counter << ":"
			   << group->m_triangle[counter*3+0] << " "
			   << group->m_triangle[counter*3+1] << " "
			   << group->m_triangle[counter*3+2] << endl;
#endif
		  counter++;
		}
	  }
	}
  }
  // create normal vector
  model->createNormalVector();

  // create origin of local
  model->createCenter();
  return model;
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-23
 */
unsigned int Read3ds::getMaterialIndex(const char* name)
  throw (char*)
{
  for( unsigned int i = 0 ; i < this->m_material.size(); i ++ ){
	if( 0 == strcmp(name, this->m_material[i]->m_name) ){
	  return i;
	}
  }
  throw "Read3ds::getMaterialIndex can not find material\n";
  return 0;
}

//=============================================================================
//	library::max::Chunk's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
Chunk::Chunk()
{
  memset(this,0,sizeof(Chunk));
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
void Chunk::display()
{
  cout << "Chunk ID: 0x" << setw(4) << setfill('0') 
	   << setbase(16) << this->m_ID << " ";
  cout << setbase(10) << "length: "<< this->m_length << endl;
}

/**
 * This method reads id and length of latter this chunk from 3ds file.
 *
 * @param fp this must be 3ds file.
 *
 * @return reading a number of byte.
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
unsigned int Chunk::readFile(FILE* fp)
{
  this->m_bytesread = 0;
  this->m_bytesread += fread(&(this->m_ID), 1, sizeof(unsigned short), fp);
  this->m_bytesread += fread(&(this->m_length), 1, sizeof(unsigned int), fp);
  
  return (this->m_bytesread);
}

/**
 * This method skips byte data in 3ds file. 
 * @param fp this must be 3ds file.
 *
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-6
 */
unsigned int Chunk::skipFile(FILE* fp)
{
  if( this->m_length > this->m_bytesread ){
	fseek(fp,this->m_length-this->m_bytesread,SEEK_CUR);
  }
  return this->m_length-Chunk::DATASIZE;
}

/**
 * This method extracts a name data from 3ds file. 
 *
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-6
 */
unsigned int library::max::getName(FILE* fp, char* name)
{
  unsigned int index = 0;
  fread(&(name[index]), 1, 1, fp);
  while( name[index++] != 0 ){
	fread(&(name[index]), 1, 1, fp);
  }

  return index;
}


//=============================================================================
//	library::max::Chunk's static member
//=============================================================================
/**
 * This static member is a size of only chunk.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
unsigned int library::max::Chunk::DATASIZE = 6;

//=============================================================================
//	library::max::Object's method
//=============================================================================
/**
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */

library::max::Object::Object()
{
  memset(this->m_name, 0, 20);
  this->m_coord.clear();
  this->m_index.clear();
  this->m_last_total_vertexsum = 0;
  this->m_vertexsum=0;
  this->m_trianglesum=0;
  this->m_uvsum = 0;
  this->m_uv.clear();
  this->m_group.clear();
}

/**
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
library::max::Object::~Object()
{
  this->m_coord.clear();
  this->m_index.clear();
  this->m_uv.clear();
  vector<library::max::FaceGroup*>::iterator ite = this->m_group.begin();
  while( ite != this->m_group.end() ){
	 delete (*ite);
	 ite++;
  }
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-22
 */
bool library::max::Object::isModelData(void)
{
  bool retval = true;
  if( m_coord.size() == 0 ){
	retval = false;
  }
  if( m_index.size() == 0 ){
	retval = false;
  }
#ifdef DEBUG
  if( retval == false ){
	cout << "    " << this->m_name << " is not 3d object" << endl;
  }
#endif
  return retval;
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-24
 */
unsigned int Object::queryGroupSum(void)
{
  unsigned int tmpsum=0;
  for( unsigned int i =0; i < this->m_group.size(); i ++ ){
	tmpsum+=this->m_group[i]->m_faces.size();
  }
  if( this->m_trianglesum != tmpsum ){
	return (this->m_group.size()+1);
  }
  return this->m_group.size();
}

//=============================================================================
//	library::max::FaceGroup's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-15
 */
FaceGroup::FaceGroup()
{
  memset(this->m_materialname,0, 20);
  this->m_faces.clear();
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-15
 */
FaceGroup::~FaceGroup()
{
  this->m_faces.clear();
}

//=============================================================================
//	library::max::Material's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-20
 */
library::max::Material::Material()
{
  memset(this,0,sizeof(Material));
}

/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-20
 */
library::max::Material::~Material()
{
}


//=============================================================================
//	ReadObj's method
//=============================================================================
/**
 * @author suzuki ryoutoku 
 * @date 2008-1-1
 * @version 1.0
 */
ReadObj::ReadObj(library::log::LogManager* arg)
  : Reader(arg)
{
  this->m_current_objvolume = NULL;
}

/**
 * @author suzuki ryoutoku 
 * @date 2008-1-1
 * @version 1.0
 */
ReadObj::~ReadObj()
{
  vector<library::objfile::ObjVolume*>::iterator ite=this->m_objvolume.begin();
  while(ite != this->m_objvolume.end() ){
	delete (*ite);
	ite++;
  }
  vector<Vector<double>*>::iterator ver = this->m_vertex.begin();
  while(ver != this->m_vertex.end() ){
	delete (*ver);
	ver++;
  }
  this->m_vertex.clear();
}

/**
 * @author suzuki ryoutoku 
 * @date 2008-1-1
 * @version 1.0
 */
Model* ReadObj::readOBJFile(const string& filename)
  throw (char*)
{
  char buffer[NumberOfCharactersLine];

  // check file extention
  if( !filename.rfind(string(".OBJ")) || !filename.rfind(string(".obj")) ){
	throw "file extention is only obj";
  }

  strcpy( this->Reader::m_filename, filename.c_str());
  char tmptime[256];
  this->m_log_manager->print("Read file name = %s\nstart time =%s\n",
							 this->Reader::m_filename,
							 this->m_log_manager->storeTime(tmptime));
  
  // open file
  if( false == this->Reader::openFile(filename) ){
	throw "cannot search obj file";
  }

  // until pointer wiill reach to end of file
  while( this->Reader::m_fin.getline( buffer, NumberOfCharactersLine ) ){
	string line(buffer);
	// skip comment line 
	if( !Reader::CheckLine(&line) ) continue;
	
	// erase a end character of line 
	if( string::npos != line.rfind(string("\r")) ){
	  line.erase( line.rfind(string("\r")) );
	}
	// eval first character of line
	switch(this->evalLine(line) ){
	  // if it's vertex infomation
	case VERTEX:
	  {
		stringstream stout(line);
		char tmp[2];
		double x,y,z;
		stout >> tmp >> x >> y >> z;
		this->m_vertex.push_back(new Vector<double>(x,y,z,1.0));
	  }
	  break;
	  // if it's group infomation
	case GROUP:
	  {
		char tmp[256];
		sscanf(line.c_str(), "g %s", tmp);
		this->setCurrentVolume(tmp);
	  }
	  break;
	  // if it's material separation infomation
	case USEMATERIAL:
	  {
		if( this->m_current_objvolume == NULL ){
		  
		  this->m_fin.close();
		  throw "ignore format";
		}
		char tmp[256];
		sscanf(line.c_str(), "usemtl %s", tmp);
		this->m_current_objvolume->setCurrentUseMaterial(tmp);
	  }
	  break;
	case FACE:
	  {
		setFaceInCurrentGroup(line);
	  }
	  break;
	}
  }

  // create model data;
  Model* model = new Model(false);
  model->m_volume_sum = this->m_objvolume.size();
  model->m_volume = new Volume[model->getM_volume_sum()];
  // copy vertex
  model->m_vertex_sum = this->m_vertex.size();
  model->m_vertex = new Vector<double>[model->getM_vertex_sum()];
  for( int i = 0; i < this->m_vertex.size(); i++){
	model->m_vertex[i] = *(this->m_vertex[i]);
  }
  // copy volume
  int globalid = 0;
  for( int i = 0; i < this->m_objvolume.size(); i++ ){
	library::objfile::ObjVolume* objvolume = this->m_objvolume[i];
	Volume* volume = &(model->m_volume[i]);
	strcpy(volume->m_name, objvolume->m_groupname);
	volume->m_group_sum = objvolume->m_objgroups.size();
	volume->m_group = new Group[volume->m_group_sum];
	// copy group
	for( int j = 0; j < objvolume->m_objgroups.size(); j++ ){
	  library::objfile::ObjGroup* objgroup = objvolume->m_objgroups[j];
	  Group* group = &(volume->m_group[j]);
	  group->initialize(objgroup->m_tri_sum,
						objgroup->m_quad_sum);

	  // copy polygon
	  int tmp_tri = 0;
	  int tmp_quad = 0;
	  for( int k = 0; k < objgroup->m_objfaces.size(); k ++ ){
		Polygon* poly = objgroup->m_objfaces[k];
		if( poly->getM_size() == 3 ){
		  group->m_triangle[tmp_tri*3+0]=poly->getM_connectivity(0);
		  group->m_triangle[tmp_tri*3+1]=poly->getM_connectivity(1);
		  group->m_triangle[tmp_tri*3+2]=poly->getM_connectivity(2);
		  group->m_triangle_globalid[tmp_tri]=globalid;
		  tmp_tri++;
		  globalid++;
		}else{
		  group->m_quadrangle[tmp_quad*4+0]=poly->getM_connectivity(0);
		  group->m_quadrangle[tmp_quad*4+1]=poly->getM_connectivity(1);
		  group->m_quadrangle[tmp_quad*4+2]=poly->getM_connectivity(2);
		  group->m_quadrangle[tmp_quad*4+3]=poly->getM_connectivity(3);
		  group->m_quadrangle_globalid[tmp_quad]=globalid;
		  tmp_quad++;
		  globalid++;
		}
	  }
	  
	}
  }
  // create normal vector
  model->createNormalVector();

  // create origin of local
  model->createCenter();
  this->Reader::m_log_manager->print("end time =%s\n",
									 this->m_log_manager->storeTime(tmptime));
  return model;
}

/**
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
unsigned int ReadObj::evalLine(const string& arg)
{
  if( string::npos != arg.find("g ") ){
	return GROUP;
  }

  if( string::npos != arg.find("usemtl ") ){
	return USEMATERIAL;
  }

  if( string::npos != arg.find("v ") ){
	return VERTEX;
  }

  if( string::npos != arg.find("f ") ){
	return FACE;
  }

  return NULL_LINE;
}

/**
 *
 * \note
 *
 *
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
bool ReadObj::setCurrentVolume(const char* arg)
{
  library::objfile::ObjVolume* v = new library::objfile::ObjVolume(arg);
  this->m_objvolume.push_back(v);
  this->m_current_objvolume = v;

  return true;
}

/**
 *
 * \note
 * In obj format , the beginini vertex global id is counted as one.
 * Therefore, When vertex global id stored polygon, subtracted one.
 * @param arg a line in obj file 
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
bool ReadObj::setFaceInCurrentGroup(const string& arg)
{
  library::objfile::ObjGroup* group 
	= this->m_current_objvolume->m_current_objgroup;
  
  int datasum = this->Reader::CountData(arg, " ") - 1;
  // if over fifth polygon, error
  if( datasum > 4 ){
	return false;
  }

  // parse line
  char line[1024];
  strcpy(line,arg.c_str());
  char part[256];

  // read each vertex index
  int connectivity[4];
  int counter = 2; // first character is "f "

  for( int i = 0;i < datasum; i++){
	int index = 0;
	// first chunk is vertex index
	while( line[counter] != '/' ){
	  part[index] = line[counter];
	  index++;
	  counter++;
	}
	part[index]='\0';
	connectivity[i] = atoi(part);
	// next chank is  index
	counter++;

	// skip next chankes are texture coord index and normal vertex index
	counter++;
	if( counter > 1024 ) break;
	while( line[counter] != ' ' && line[counter] != '\0'){//data last
	  if( counter > 1024 ) break;
	  counter++;
	}
  }

  if( datasum == 3 ){
	group->m_tri_sum++;
	Polygon* poly = new Polygon(0,3);
	// shift stating point form 1 into 0
	poly->setM_connectivity(0,connectivity[0]-1);
	poly->setM_connectivity(1,connectivity[1]-1);
	poly->setM_connectivity(2,connectivity[2]-1);
	group->m_objfaces.push_back(poly);
  }else if(datasum == 4 ){
	group->m_quad_sum++;
	Polygon* poly = new Polygon(0,4);
	// shift stating point form 1 into 0
	poly->setM_connectivity(0,connectivity[0]-1);
	poly->setM_connectivity(1,connectivity[1]-1);
	poly->setM_connectivity(2,connectivity[2]-1);
	poly->setM_connectivity(3,connectivity[3]-1);
	group->m_objfaces.push_back(poly);
  }
#if 0
  for( int i = 0; i < datasum; i++){
	cout << connectivity[i] << " "; 
  }
  cout << endl;
#endif
  return true;
}

//=============================================================================
//	ObjVolume's method
//=============================================================================
/**
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
library::objfile::ObjVolume::ObjVolume(const char* arg)
{
  strcpy(this->m_groupname, arg);
  this->m_objgroups.push_back(new library::objfile::ObjGroup("defalut"));
  this->m_current_objgroup = this->m_objgroups[0];
}

/**
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
library::objfile::ObjVolume::~ObjVolume()
{
  vector<library::objfile::ObjGroup*>::iterator ite=this->m_objgroups.begin();
  while( ite != this->m_objgroups.end() ){
	delete (*ite);
	ite++;
  }
  this->m_objgroups.clear();
}

/**
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
unsigned int library::objfile::ObjVolume::countEnableGroup(void)
{
  unsigned int retval = 0;
  vector<library::objfile::ObjGroup*>::iterator ite=this->m_objgroups.begin();
  while( ite != this->m_objgroups.end() ){
	if( (*ite)->m_objfaces.size() != 0 ){
	  retval++;
	}
	ite++;
  }
  return retval;
}

/**
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
bool library::objfile::ObjVolume::setCurrentUseMaterial(const char* arg)
{
  vector<library::objfile::ObjGroup*>::iterator ite=this->m_objgroups.begin();
  while( ite != this->m_objgroups.end() ){
	if( NULL != strstr((*ite)->m_keyword, arg) ){
	  this->m_current_objgroup = (*ite);
	}
	ite++;
  }
  library::objfile::ObjGroup* g = new ObjGroup(arg);
  this->m_current_objgroup = g;
  this->m_objgroups.push_back(g);
  return true;
}

//=============================================================================
//	ObjGroup's method
//=============================================================================
/**
 *
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
library::objfile::ObjGroup::ObjGroup(const char* arg)
{
  strcpy(this->m_keyword, arg);
  this->m_objfaces.clear();
  this->m_quad_sum=0;
  this->m_tri_sum=0;
}

/**
 *
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
library::objfile::ObjGroup::~ObjGroup()
{
  vector<library::graphics::Polygon*>::iterator ite= this->m_objfaces.begin();
  while( ite != this->m_objfaces.end() ){
	delete (*ite);
	ite++;
  }
  this->m_objfaces.clear();
}
